/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(
    dead_code,
    mutable_transmutes,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    unused_unsafe,
    clashing_extern_declarations,
    clippy::never_loop
)]
use crate::common::*;
extern "C" {
    fn fclose(__stream: *mut FILE) -> libc::c_int;
    fn fopen(_: *const libc::c_char, _: *const libc::c_char) -> *mut FILE;
    fn asprintf(__ptr: *mut *mut libc::c_char, __fmt: *const libc::c_char, _: ...) -> libc::c_int;
    fn getdelim(
        __lineptr: *mut *mut libc::c_char,
        __n: *mut size_t,
        __delimiter: libc::c_int,
        __stream: *mut FILE,
    ) -> __ssize_t;
    fn calloc(_: libc::c_ulong, _: libc::c_ulong) -> *mut libc::c_void;
    fn free(_: *mut libc::c_void);
    fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn strncmp(_: *const libc::c_char, _: *const libc::c_char, _: libc::c_ulong) -> libc::c_int;
    fn strdup(_: *const libc::c_char) -> *mut libc::c_char;
    fn strndup(_: *const libc::c_char, _: libc::c_ulong) -> *mut libc::c_char;
    fn strchr(_: *const libc::c_char, _: libc::c_int) -> *mut libc::c_char;
    fn strcasecmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn __ctype_b_loc() -> *mut *const libc::c_ushort;
    fn sudo_warnx_nodebug_v1(fmt: *const libc::c_char, _: ...);
    static mut sudo_defs_table: [sudo_defs_types; 0];
    fn free_userspecs(usl: *mut userspec_list);
    fn init_parse_tree(
        parse_tree: *mut sudoers_parse_tree,
        shost: *const libc::c_char,
        lhost: *const libc::c_char,
    );
    fn free_parse_tree(parse_tree: *mut sudoers_parse_tree);
    fn netgr_matches(
        netgr: *const libc::c_char,
        lhost: *const libc::c_char,
        shost: *const libc::c_char,
        user: *const libc::c_char,
    ) -> bool;
    fn usergr_matches(
        group: *const libc::c_char,
        user: *const libc::c_char,
        pw: *const passwd,
    ) -> bool;
    fn userpw_matches(
        sudoers_user: *const libc::c_char,
        user: *const libc::c_char,
        pw: *const passwd,
    ) -> bool;
    fn rcstr_delref(s: *const libc::c_char);
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    fn sudo_pw_addref(_: *mut passwd);
    fn sudo_pw_delref(_: *mut passwd);
    static mut sudo_user: sudo_user;
    fn rcstr_dup(src: *const libc::c_char) -> *mut libc::c_char;
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    fn sudo_ldap_add_default(
        var: *const libc::c_char,
        val: *const libc::c_char,
        op: libc::c_int,
        source: *mut libc::c_char,
        defs: *mut defaults_list,
    ) -> bool;
    fn sudo_ldap_parse_option(
        optstr: *mut libc::c_char,
        varp: *mut *mut libc::c_char,
        valp: *mut *mut libc::c_char,
    ) -> libc::c_int;
    fn sudo_ldap_role_to_priv(
        cn: *const libc::c_char,
        hosts: *mut libc::c_void,
        runasusers: *mut libc::c_void,
        runasgroups: *mut libc::c_void,
        cmnds: *mut libc::c_void,
        opts: *mut libc::c_void,
        notbefore: *const libc::c_char,
        notafter: *const libc::c_char,
        warnings: bool,
        store_options: bool,
        iter: sudo_ldap_iter_t,
    ) -> *mut privilege;
    fn sudo_dso_strerror_v1() -> *mut libc::c_char;
    fn sudo_dso_unload_v1(handle: *mut libc::c_void) -> libc::c_int;
    fn sudo_dso_findsym_v1(
        handle: *mut libc::c_void,
        symbol: *const libc::c_char,
    ) -> *mut libc::c_void;
    fn sudo_dso_load_v1(path: *const libc::c_char, mode: libc::c_int) -> *mut libc::c_void;
}

pub const ENOMEM: libc::c_int = 12;
pub const EFAULT: libc::c_int = 14;

pub type sudo_ldap_iter_t =
    Option<unsafe extern "C" fn(*mut *mut libc::c_void) -> *mut libc::c_char>;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct sss_sudo_attr {
    pub name: *mut libc::c_char,
    pub values: *mut *mut libc::c_char,
    pub num_values: libc::c_uint,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sss_sudo_rule {
    pub num_attrs: libc::c_uint,
    pub attrs: *mut sss_sudo_attr,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sss_sudo_result {
    pub num_rules: libc::c_uint,
    pub rules: *mut sss_sudo_rule,
}
pub type sss_sudo_send_recv_t = Option<
    unsafe extern "C" fn(
        uid_t,
        *const libc::c_char,
        *const libc::c_char,
        *mut uint32_t,
        *mut *mut sss_sudo_result,
    ) -> libc::c_int,
>;
pub type sss_sudo_send_recv_defaults_t = Option<
    unsafe extern "C" fn(
        uid_t,
        *const libc::c_char,
        *mut uint32_t,
        *mut *mut libc::c_char,
        *mut *mut sss_sudo_result,
    ) -> libc::c_int,
>;
pub type sss_sudo_free_result_t = Option<unsafe extern "C" fn(*mut sss_sudo_result) -> ()>;
pub type sss_sudo_get_values_t = Option<
    unsafe extern "C" fn(
        *mut sss_sudo_rule,
        *const libc::c_char,
        *mut *mut *mut libc::c_char,
    ) -> libc::c_int,
>;
pub type sss_sudo_free_values_t = Option<unsafe extern "C" fn(*mut *mut libc::c_char) -> ()>;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_sss_handle {
    pub domainname: *mut libc::c_char,
    pub ipa_host: *mut libc::c_char,
    pub ipa_shost: *mut libc::c_char,
    pub pw: *mut passwd,
    pub ssslib: *mut libc::c_void,
    pub parse_tree: sudoers_parse_tree,
    pub fn_send_recv: sss_sudo_send_recv_t,
    pub fn_send_recv_defaults: sss_sudo_send_recv_defaults_t,
    pub fn_free_result: sss_sudo_free_result_t,
    pub fn_get_values: sss_sudo_get_values_t,
    pub fn_free_values: sss_sudo_free_values_t,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_nss {
    pub entries: C2RustUnnamed_7,
    pub open: Option<unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int>,
    pub close: Option<unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int>,
    pub parse: Option<unsafe extern "C" fn(*mut sudo_nss) -> *mut sudoers_parse_tree>,
    pub query: Option<unsafe extern "C" fn(*mut sudo_nss, *mut passwd) -> libc::c_int>,
    pub getdefs: Option<unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int>,
    pub handle: *mut libc::c_void,
    pub parse_tree: *mut sudoers_parse_tree,
    pub ret_if_found: bool,
    pub ret_if_notfound: bool,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_7 {
    pub tqe_next: *mut sudo_nss,
    pub tqe_prev: *mut *mut sudo_nss,
}
#[macro_export]
macro_rules! _PATH_SSSD_CONF {
    () => {
        b"/etc/sssd/sssd.conf\0" as *const u8 as *const libc::c_char
    };
}

fn get_ipa_hostname(
    mut shostp: *mut *mut libc::c_char,
    mut lhostp: *mut *mut libc::c_char,
) -> libc::c_int {
    let mut linesize: size_t = 0 as libc::c_int as size_t;
    let mut lhost: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut shost: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut line: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut ret: libc::c_int = false as libc::c_int;
    let mut len: ssize_t = 0;
    let mut fp: *mut FILE = std::ptr::null_mut::<FILE>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_SSSD!());

        fp = fopen(
            _PATH_SSSD_CONF!(),
            b"r\0" as *const u8 as *const libc::c_char,
        );
        if !fp.is_null() {
            loop {
                len = getdelim(&mut line, &mut linesize, '\n' as i32, fp);
                if len == -(1 as libc::c_int) as libc::c_long {
                    break;
                }
                let mut cp: *mut libc::c_char = line;

                /* Trim trailing and leading spaces. */
                while len > 0 as libc::c_int as libc::c_long
                    && isspace!(*line.offset((len - 1 as libc::c_int as libc::c_long) as isize))
                        != 0
                {
                    len -= 1;
                    *line.offset(len as isize) = '\0' as i32 as libc::c_char;
                }
                while isspace!(*cp as libc::c_uchar as libc::c_int as isize) != 0 {
                    cp = cp.offset(1);
                }

                /*
                 * Match ipa_hostname = foo
                 * Note: currently ignores the domain (XXX)
                 */

                if strncmp(
                    cp,
                    b"ipa_hostname\0" as *const u8 as *const libc::c_char,
                    12 as libc::c_int as libc::c_ulong,
                ) != 0 as libc::c_int
                /*if !(strncmp(
                    cp,
                    b"ipa_hostname\0" as *const u8 as *const libc::c_char,
                    12 as libc::c_int as libc::c_ulong,
                ) == 0 as libc::c_int)*/
                {
                    continue;
                }
                cp = cp.offset(12 as libc::c_int as isize);
                /* Trim " = " after "ipa_hostname" */
                while isblank!(*cp as libc::c_uchar as libc::c_int as isize) != 0 {
                    cp = cp.offset(1);
                }
                cp = cp.offset(1);
                if *cp as libc::c_int != '=' as i32 {
                    continue;
                }
                while isblank!(*cp as libc::c_uchar as libc::c_int as isize) != 0 {
                    cp = cp.offset(1);
                }
                /* Ignore empty value */
                if *cp as libc::c_int == '\0' as i32 {
                    continue;
                }
                lhost = strdup(cp);
                if !lhost.is_null() && {
                    cp = strchr(lhost, '.' as i32);
                    !cp.is_null()
                } {
                    shost = strndup(lhost, cp.offset_from(lhost) as libc::c_long as size_t);
                } else {
                    shost = lhost;
                }
                if !shost.is_null() && !lhost.is_null() {
                    sudo_debug_printf!(
                        SUDO_DEBUG_INFO,
                        b"ipa_hostname %s overrides %s\0" as *const u8 as *const libc::c_char,
                        lhost,
                        sudo_user.host
                    );
                    *shostp = shost;
                    *lhostp = lhost;
                    ret = true as libc::c_int;
                } else {
                    sudo_warnx!(
                        b"%s: %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                    );
                    free(shost as *mut libc::c_void);
                    free(lhost as *mut libc::c_void);
                    ret = -(1 as libc::c_int);
                }
                break;
            }
            fclose(fp);
            free(line as *mut libc::c_void);
        }

        debug_return_int!(ret);
    } //unsafe
}

/*
 * SSSD doesn't handle netgroups, we have to ensure they are correctly filtered
 * in sudo. The rules may contain mixed sudoUser specification so we have to
 * check not only for netgroup membership but also for user and group matches.
 * Otherwise, a netgroup non-match could override a user/group match.
 */
fn sudo_sss_check_user(mut handle: *mut sudo_sss_handle, mut rule: *mut sss_sudo_rule) -> bool {
    unsafe {
        let mut host: *const libc::c_char = if !((*handle).ipa_host).is_null() {
            (*handle).ipa_host
        } else {
            sudo_user.runhost
        };
        let mut shost: *const libc::c_char = if !((*handle).ipa_shost).is_null() {
            (*handle).ipa_shost
        } else {
            sudo_user.srunhost
        };
        let mut val_array: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
        let mut i: libc::c_int = false as libc::c_int;
        let mut ret: libc::c_int = false as libc::c_int;
        debug_decl!(SUDOERS_DEBUG_SSSD!());

        if rule.is_null() {
            debug_return_bool!(false);
        }

        match ((*handle).fn_get_values).expect("non-null function pointer")(
            rule,
            b"sudoUser\0" as *const u8 as *const libc::c_char,
            &mut val_array,
        ) {
            0 => {}
            ENOENT => {
                sudo_debug_printf!(
                    SUDO_DEBUG_INFO,
                    b"No result.\0" as *const u8 as *const libc::c_char
                );
                debug_return_bool!(false);
            }
            _ => {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR,
                    b"handle->fn_get_values(sudoUser): != 0\0" as *const u8 as *const libc::c_char
                );
                debug_return_bool!(false);
            }
        }

        /* Walk through sudoUser values.  */
        i = 0 as libc::c_int;
        while !(*val_array.offset(i as isize)).is_null() && ret == 0 {
            let mut val: *const libc::c_char = *val_array.offset(i as isize);

            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG,
                b"val[%d]=%s\0" as *const u8 as *const libc::c_char,
                i,
                val
            );
            match *val as u8 as char {
                '+' => {
                    /* Netgroup spec found, check membership. */
                    if netgr_matches(
                        val,
                        if def_netgroup_tuple!() != 0 {
                            host
                        } else {
                            std::ptr::null::<libc::c_char>()
                        },
                        if def_netgroup_tuple!() != 0 {
                            shost
                        } else {
                            std::ptr::null::<libc::c_char>()
                        },
                        (*(*handle).pw).pw_name,
                    ) {
                        ret = true as libc::c_int;
                    }
                }
                '%' => {
                    /* User group found, check membership. */
                    if usergr_matches(val, (*(*handle).pw).pw_name, (*handle).pw) {
                        ret = true as libc::c_int;
                    }
                }
                _ => {
                    /* Not a netgroup or user group. */
                    if strcmp(val, b"ALL\0" as *const u8 as *const libc::c_char) == 0 as libc::c_int
                        || userpw_matches(val, (*(*handle).pw).pw_name, (*handle).pw) as libc::c_int
                            != 0
                    {
                        ret = true as libc::c_int;
                    }
                }
            }
            sudo_debug_printf!(
                SUDO_DEBUG_DIAG,
                b"sssd/ldap sudoUser '%s' ... %s (%s)\0" as *const u8 as *const libc::c_char,
                val,
                if ret != 0 {
                    b"MATCH!\0" as *const u8 as *const libc::c_char
                } else {
                    b"not\0" as *const u8 as *const libc::c_char
                },
                (*(*handle).pw).pw_name
            );
            i += 1;
        }
        ((*handle).fn_free_values).expect("non-null function pointer")(val_array);

        debug_return_bool!(ret != 0);
    } //usnafe
}

unsafe extern "C" fn val_array_iter(mut vp: *mut *mut libc::c_void) -> *mut libc::c_char {
    let mut val_array: *mut *mut libc::c_char = *vp as *mut *mut libc::c_char;

    *vp = val_array.offset(1 as libc::c_int as isize) as *mut libc::c_void;

    *val_array
}

fn sss_to_sudoers(mut handle: *mut sudo_sss_handle, mut sss_result: *mut sss_sudo_result) -> bool {
    let mut us: *mut userspec = std::ptr::null_mut::<userspec>();
    let mut m: *mut member = std::ptr::null_mut::<member>();
    let mut i: libc::c_uint = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_SSSD!());

        'oom: loop {
            /* We only have a single userspec */
            us = calloc(
                1 as libc::c_int as libc::c_ulong,
                ::core::mem::size_of::<userspec>() as libc::c_ulong,
            ) as *mut userspec;
            if us.is_null() {
                break 'oom;
            }
            (*us).users.tqh_first = std::ptr::null_mut::<member>();
            (*us).users.tqh_last = &mut (*us).users.tqh_first;
            (*us).privileges.tqh_first = std::ptr::null_mut::<privilege>();
            (*us).privileges.tqh_last = &mut (*us).privileges.tqh_first;
            (*us).comments.stqh_first = std::ptr::null_mut::<sudoers_comment>();
            (*us).comments.stqh_last = &mut (*us).comments.stqh_first;
            (*us).entries.tqe_next = std::ptr::null_mut::<userspec>();
            (*us).entries.tqe_prev = (*handle).parse_tree.userspecs.tqh_last;
            *(*handle).parse_tree.userspecs.tqh_last = us;
            (*handle).parse_tree.userspecs.tqh_last = &mut (*us).entries.tqe_next;

            /* We only include rules where the user matches. */
            m = calloc(
                1 as libc::c_int as libc::c_ulong,
                ::core::mem::size_of::<member>() as libc::c_ulong,
            ) as *mut member;
            if m.is_null() {
                break 'oom;
            }
            (*m).type0 = ALL as libc::c_short;
            (*m).entries.tqe_next = std::ptr::null_mut::<member>();
            (*m).entries.tqe_prev = (*us).users.tqh_last;
            *(*us).users.tqh_last = m;
            (*us).users.tqh_last = &mut (*m).entries.tqe_next;

            /*
             * Treat each sudoRole as a separate privilege.
             *
             * Sssd has already sorted the rules in descending order.
             * The conversion to a sudoers parse tree requires that entries be
             * in *ascending* order so we we iterate from last to first.
             */
            i = (*sss_result).num_rules;
            loop {
                i = i.wrapping_sub(1);
                if i <= 0 as libc::c_int as libc::c_uint {
                    break;
                }
                let mut rule: *mut sss_sudo_rule = ((*sss_result).rules).offset(i as isize);
                let mut cmnds: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
                let mut runasusers: *mut *mut libc::c_char =
                    std::ptr::null_mut::<*mut libc::c_char>();
                let mut runasgroups: *mut *mut libc::c_char =
                    std::ptr::null_mut::<*mut libc::c_char>();
                let mut opts: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
                let mut notbefore: *mut *mut libc::c_char =
                    std::ptr::null_mut::<*mut libc::c_char>();
                let mut notafter: *mut *mut libc::c_char =
                    std::ptr::null_mut::<*mut libc::c_char>();
                let mut hosts: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
                let mut cn_array: *mut *mut libc::c_char =
                    std::ptr::null_mut::<*mut libc::c_char>();
                let mut cn: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
                let mut priv_0: *mut privilege = std::ptr::null_mut::<privilege>();

                /*
                 * We don't know whether a rule was included due to a user/group
                 * match or because it contained a netgroup.
                 */
                if !sudo_sss_check_user(handle, rule) {
                    continue;
                }

                'cleanup: loop {
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"sudoCommand\0" as *const u8 as *const libc::c_char,
                        &mut cmnds,
                    ) {
                        0 => {}
                        ENOENT => {
                            /* Ignore sudoRole without sudoCommand. */
                            continue;
                        }
                        _ => {
                            break 'cleanup;
                        }
                    }

                    /* Get the entry's dn for long format printing. */
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"cn\0" as *const u8 as *const libc::c_char,
                        &mut cn_array,
                    ) {
                        0 => {
                            cn = *cn_array.offset(0 as libc::c_int as isize);
                        }
                        ENOENT => {}
                        _ => {
                            break 'cleanup;
                        }
                    }

                    /* Get sudoHost */
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"sudoHost\0" as *const u8 as *const libc::c_char,
                        &mut hosts,
                    ) {
                        0 | ENOENT => {}
                        _ => {
                            break 'cleanup;
                        }
                    }

                    /* Get sudoRunAsUser / sudoRunAs */
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"sudoRunAsUser\0" as *const u8 as *const libc::c_char,
                        &mut runasusers,
                    ) {
                        0 => {}
                        ENOENT => {
                            match ((*handle).fn_get_values).expect("non-null function pointer")(
                                rule,
                                b"sudoRunAs\0" as *const u8 as *const libc::c_char,
                                &mut runasusers,
                            ) {
                                0 | ENOENT => {}
                                _ => {
                                    break 'cleanup;
                                }
                            }
                        }
                        _ => {
                            break 'cleanup;
                        }
                    }

                    /* Get sudoRunAsGroup */
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"sudoRunAsGroup\0" as *const u8 as *const libc::c_char,
                        &mut runasgroups,
                    ) {
                        0 | ENOENT => {}
                        _ => {
                            break 'cleanup;
                        }
                    }

                    /* Get sudoNotBefore */
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"sudoNotBefore\0" as *const u8 as *const libc::c_char,
                        &mut notbefore,
                    ) {
                        0 | ENOENT => {}
                        _ => {
                            break 'cleanup;
                        }
                    }

                    /* Get sudoNotAfter */
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"sudoNotAfter\0" as *const u8 as *const libc::c_char,
                        &mut notafter,
                    ) {
                        0 | ENOENT => {}
                        _ => {
                            break 'cleanup;
                        }
                    }

                    /* Parse sudoOptions. */
                    match ((*handle).fn_get_values).expect("non-null function pointer")(
                        rule,
                        b"sudoOption\0" as *const u8 as *const libc::c_char,
                        &mut opts,
                    ) {
                        0 | ENOENT => {}
                        _ => {
                            break 'cleanup;
                        }
                    }

                    priv_0 = sudo_ldap_role_to_priv(
                        cn,
                        hosts as *mut libc::c_void,
                        runasusers as *mut libc::c_void,
                        runasgroups as *mut libc::c_void,
                        cmnds as *mut libc::c_void,
                        opts as *mut libc::c_void,
                        if !notbefore.is_null() {
                            *notbefore.offset(0 as libc::c_int as isize)
                        } else {
                            std::ptr::null_mut::<libc::c_char>()
                        },
                        if !notafter.is_null() {
                            *notafter.offset(0 as libc::c_int as isize)
                        } else {
                            std::ptr::null_mut::<libc::c_char>()
                        },
                        false,
                        true,
                        Some(
                            val_array_iter
                                as unsafe extern "C" fn(
                                    *mut *mut libc::c_void,
                                )
                                    -> *mut libc::c_char,
                        ),
                    );
                    break;
                }

                //cleanup:
                if !cn_array.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(cn_array);
                }
                if !cmnds.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(cmnds);
                }
                if !hosts.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(hosts);
                }
                if !runasusers.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(runasusers);
                }
                if !runasgroups.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(runasgroups);
                }
                if !opts.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(opts);
                }
                if !notbefore.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(notbefore);
                }
                if !notafter.is_null() {
                    ((*handle).fn_free_values).expect("non-null function pointer")(notafter);
                }

                if priv_0.is_null() {
                    break 'oom;
                }
                (*priv_0).entries.tqe_next = std::ptr::null_mut::<privilege>();
                (*priv_0).entries.tqe_prev = (*us).privileges.tqh_last;
                *(*us).privileges.tqh_last = priv_0;
                (*us).privileges.tqh_last = &mut (*priv_0).entries.tqe_next;
            }

            debug_return_bool!(true);
        }

        //oom:
        sudo_warnx!(
            b"%s: %s\0" as *const u8 as *const libc::c_char,
            get_function_name!(),
            b"unable to allocate memory\0" as *const u8 as *const libc::c_char
        );
        free_userspecs(&mut (*handle).parse_tree.userspecs);
        debug_return_bool!(false);
    } //unsafe
}

fn sudo_sss_parse_options(
    mut handle: *mut sudo_sss_handle,
    mut rule: *mut sss_sudo_rule,
    mut defs: *mut defaults_list,
) -> bool {
    let mut i: libc::c_int = 0;
    let mut source: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut ret: bool = 0 as libc::c_int != 0;
    let mut val_array: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
    let mut cn_array: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_SSSD!());

        if rule.is_null() {
            debug_return_bool!(true);
        }

        'done: loop {
            'oom: loop {
                match ((*handle).fn_get_values).expect("non-null function pointer")(
                    rule,
                    b"sudoOption\0" as *const u8 as *const libc::c_char,
                    &mut val_array,
                ) {
                    0 => {}
                    ENOENT => {
                        sudo_debug_printf!(
                            SUDO_DEBUG_INFO,
                            b"No result.\0" as *const u8 as *const libc::c_char
                        );
                        debug_return_bool!(true);
                    }
                    ENOMEM => {
                        break 'oom;
                    }
                    _ => {
                        sudo_debug_printf!(
                            SUDO_DEBUG_ERROR,
                            b"handle->fn_get_values(sudoOption): != 0\0" as *const u8
                                as *const libc::c_char
                        );
                        debug_return_bool!(false);
                    }
                }

                /* Use sudoRole in place of file name in defaults. */
                if ((*handle).fn_get_values).expect("non-null function pointer")(
                    rule,
                    b"cn\0" as *const u8 as *const libc::c_char,
                    &mut cn_array,
                ) == 0 as libc::c_int
                {
                    if !(*cn_array.offset(0 as libc::c_int as isize)).is_null() {
                        let mut cp: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
                        if asprintf(
                            &mut cp as *mut *mut libc::c_char,
                            b"sudoRole %s\0" as *const u8 as *const libc::c_char,
                            *cn_array.offset(0 as libc::c_int as isize),
                        ) == -(1 as libc::c_int)
                        {
                            break 'oom;
                        }
                        source = rcstr_dup(cp);
                        free(cp as *mut libc::c_void);
                        if source.is_null() {
                            break 'oom;
                        }
                    }
                    ((*handle).fn_free_values).expect("non-null function pointer")(cn_array);
                    cn_array = std::ptr::null_mut::<*mut libc::c_char>();
                }
                if source.is_null() {
                    source = rcstr_dup(b"sudoRole UNKNOWN\0" as *const u8 as *const libc::c_char);
                    if source.is_null() {
                        break 'oom;
                    }
                }

                /* Walk through options, appending to defs. */
                i = 0 as libc::c_int;
                loop {
                    if (*val_array.offset(i as isize)).is_null() {
                        break;
                    }
                    let mut var: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
                    let mut val: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
                    let mut op: libc::c_int = 0;

                    op = sudo_ldap_parse_option(*val_array.offset(i as isize), &mut var, &mut val);
                    if !sudo_ldap_add_default(var, val, op, source, defs) {
                        break 'oom;
                    }
                    i += 1;
                }
                ret = true;
                break 'done;
            }
            //oom:
            sudo_warnx!(
                b"%s: %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                b"unable to allocate memory\0" as *const u8 as *const libc::c_char
            );
            break;
        }

        //done:
        rcstr_delref(source);
        ((*handle).fn_free_values).expect("non-null function pointer")(val_array);
        debug_return_bool!(ret);
    } //unsafe
}

fn sudo_sss_result_get(mut nss: *mut sudo_nss, mut pw: *mut passwd) -> *mut sss_sudo_result {
    unsafe {
        let mut handle: *mut sudo_sss_handle = (*nss).handle as *mut sudo_sss_handle;
        let mut sss_result: *mut sss_sudo_result = std::ptr::null_mut::<sss_sudo_result>();
        let mut sss_error: uint32_t = 0 as libc::c_int as uint32_t;
        let mut rc: uint32_t = 0;
        debug_decl!(SUDOERS_DEBUG_SSSD!());

        sudo_debug_printf!(
            SUDO_DEBUG_DIAG,
            b"  username=%s\0" as *const u8 as *const libc::c_char,
            (*pw).pw_name
        );
        sudo_debug_printf!(
            SUDO_DEBUG_DIAG,
            b"domainname=%s\0" as *const u8 as *const libc::c_char,
            if !((*handle).domainname).is_null() {
                (*handle).domainname as *const libc::c_char
            } else {
                b"NULL\0" as *const u8 as *const libc::c_char
            }
        );

        rc = ((*handle).fn_send_recv).expect("non-null function pointer")(
            (*pw).pw_uid,
            (*pw).pw_name,
            (*handle).domainname,
            &mut sss_error,
            &mut sss_result,
        ) as uint32_t;

        match rc as libc::c_int {
            0 => match sss_error as libc::c_int {
                0 => {
                    if !sss_result.is_null() {
                        sudo_debug_printf!(
                            SUDO_DEBUG_INFO,
                            b"Received %u rule(s)\0" as *const u8 as *const libc::c_char,
                            (*sss_result).num_rules
                        );
                    } else {
                        sudo_debug_printf!(
                            SUDO_DEBUG_ERROR,
                            b"Internal error: sss_result == NULL && sss_error == 0\0" as *const u8
                                as *const libc::c_char
                        );
                        debug_return_ptr!(std::ptr::null_mut::<sss_sudo_result>());
                    }
                }
                _ => {
                    if sss_error as libc::c_int == ENOENT {
                        sudo_debug_printf!(
                            SUDO_DEBUG_INFO,
                            b"The user was not found in SSSD.\0" as *const u8
                                as *const libc::c_char
                        );
                        debug_return_ptr!(std::ptr::null_mut::<sss_sudo_result>());
                    }
                    sudo_debug_printf!(
                        SUDO_DEBUG_ERROR,
                        b"sss_error=%u\0" as *const u8 as *const libc::c_char,
                        sss_error
                    );
                    debug_return_ptr!(std::ptr::null_mut::<sss_sudo_result>());
                }
            },
            _ => {
                if rc as libc::c_int == ENOMEM {
                    sudo_warnx!(
                        b"%s: %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                    );
                }
                /* FALLTHROUGH */
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR,
                    b"handle->fn_send_recv: rc=%d\0" as *const u8 as *const libc::c_char,
                    rc
                );
                debug_return_ptr!(std::ptr::null_mut::<sss_sudo_result>());
            }
        }

        debug_return_ptr!(sss_result);
    } //unsafe
}

/* sudo_nss implementation */
unsafe extern "C" fn sudo_sss_close(mut nss: *mut sudo_nss) -> libc::c_int {
    let mut handle: *mut sudo_sss_handle = (*nss).handle as *mut sudo_sss_handle;
    debug_decl!(SUDOERS_DEBUG_SSSD!());

    if !handle.is_null() {
        sudo_dso_unload_v1((*handle).ssslib);
        if !((*handle).pw).is_null() {
            sudo_pw_delref((*handle).pw);
        }
        free((*handle).ipa_host as *mut libc::c_void);
        if (*handle).ipa_host != (*handle).ipa_shost {
            free((*handle).ipa_shost as *mut libc::c_void);
        }
        free_parse_tree(&mut (*handle).parse_tree);
        free(handle as *mut libc::c_void);
        (*nss).handle = std::ptr::null_mut::<libc::c_void>();
    }
    debug_return_int!(0);
}

unsafe extern "C" fn sudo_sss_open(mut nss: *mut sudo_nss) -> libc::c_int {
    let mut handle: *mut sudo_sss_handle = std::ptr::null_mut::<sudo_sss_handle>();
    static mut path: [libc::c_char; 26] = unsafe {
        *::core::mem::transmute::<&[u8; 26], &[libc::c_char; 26]>(b"/usr/lib64/libsss_sudo.so\0")
    };
    debug_decl!(SUDOERS_DEBUG_SSSD!());

    if !((*nss).handle).is_null() {
        sudo_debug_printf!(
            SUDO_DEBUG_ERROR,
            b"%s: called with non-NULL handle %p\0" as *const u8 as *const libc::c_char,
            get_function_name!(),
            (*nss).handle
        );
        sudo_sss_close(nss);
    }

    /* Create a handle container. */
    handle = calloc(
        1 as libc::c_int as libc::c_ulong,
        ::core::mem::size_of::<sudo_sss_handle>() as libc::c_ulong,
    ) as *mut sudo_sss_handle;
    if handle.is_null() {
        sudo_warnx!(
            b"%s: %s\0" as *const u8 as *const libc::c_char,
            get_function_name!(),
            b"unable to allocate memory\0" as *const u8 as *const libc::c_char
        );
        debug_return_int!(ENOMEM);
    }

    /* Load symbols */
    (*handle).ssslib = sudo_dso_load_v1(path.as_ptr(), 0x1 as libc::c_int);
    if ((*handle).ssslib).is_null() {
        let mut errstr: *const libc::c_char = sudo_dso_strerror_v1();
        sudo_warnx!(
            b"unable to load %s: %s\0" as *const u8 as *const libc::c_char,
            path.as_ptr(),
            if !errstr.is_null() {
                errstr
            } else {
                b"unknown error\0" as *const u8 as *const libc::c_char
            }
        );
        sudo_warnx!(
            b"unable to initialize SSS source. Is SSSD installed on your machine?\0" as *const u8
                as *const libc::c_char,
        );
        free(handle as *mut libc::c_void);
        debug_return_int!(EFAULT);
    }

    (*handle).fn_send_recv =
        ::core::mem::transmute::<*mut libc::c_void, sss_sudo_send_recv_t>(sudo_dso_findsym_v1(
            (*handle).ssslib,
            b"sss_sudo_send_recv\0" as *const u8 as *const libc::c_char,
        ));
    if ((*handle).fn_send_recv).is_none() {
        sudo_warnx!(
            b"unable to find symbol \"%s\" in %s\0" as *const u8 as *const libc::c_char,
            path.as_ptr(),
            b"sss_sudo_send_recv\0" as *const u8 as *const libc::c_char
        );
        free(handle as *mut libc::c_void);
        debug_return_int!(EFAULT);
    }

    (*handle).fn_send_recv_defaults = ::core::mem::transmute::<
        *mut libc::c_void,
        sss_sudo_send_recv_defaults_t,
    >(sudo_dso_findsym_v1(
        (*handle).ssslib,
        b"sss_sudo_send_recv_defaults\0" as *const u8 as *const libc::c_char,
    ));
    if ((*handle).fn_send_recv_defaults).is_none() {
        sudo_warnx!(
            b"unable to find symbol \"%s\" in %s\0" as *const u8 as *const libc::c_char,
            path.as_ptr(),
            b"sss_sudo_send_recv_defaults\0" as *const u8 as *const libc::c_char
        );
        free(handle as *mut libc::c_void);
        debug_return_int!(EFAULT);
    }

    (*handle).fn_free_result =
        ::core::mem::transmute::<*mut libc::c_void, sss_sudo_free_result_t>(sudo_dso_findsym_v1(
            (*handle).ssslib,
            b"sss_sudo_free_result\0" as *const u8 as *const libc::c_char,
        ));
    if ((*handle).fn_free_result).is_none() {
        sudo_warnx!(
            b"unable to find symbol \"%s\" in %s\0" as *const u8 as *const libc::c_char,
            path.as_ptr(),
            b"sss_sudo_free_result\0" as *const u8 as *const libc::c_char
        );
        free(handle as *mut libc::c_void);
        debug_return_int!(EFAULT);
    }

    (*handle).fn_get_values =
        ::core::mem::transmute::<*mut libc::c_void, sss_sudo_get_values_t>(sudo_dso_findsym_v1(
            (*handle).ssslib,
            b"sss_sudo_get_values\0" as *const u8 as *const libc::c_char,
        ));
    if ((*handle).fn_get_values).is_none() {
        sudo_warnx!(
            b"unable to find symbol \"%s\" in %s\0" as *const u8 as *const libc::c_char,
            path.as_ptr(),
            b"sss_sudo_get_values\0" as *const u8 as *const libc::c_char
        );
        free(handle as *mut libc::c_void);
        debug_return_int!(EFAULT);
    }

    (*handle).fn_free_values =
        ::core::mem::transmute::<*mut libc::c_void, sss_sudo_free_values_t>(sudo_dso_findsym_v1(
            (*handle).ssslib,
            b"sss_sudo_free_values\0" as *const u8 as *const libc::c_char,
        ));
    if ((*handle).fn_free_values).is_none() {
        sudo_warnx!(
            b"unable to find symbol \"%s\" in %s\0" as *const u8 as *const libc::c_char,
            path.as_ptr(),
            b"sss_sudo_free_values\0" as *const u8 as *const libc::c_char
        );
        free(handle as *mut libc::c_void);
        debug_return_int!(EFAULT);
    }

    /*
     * If runhost is the same as the local host, check for ipa_hostname
     * in sssd.conf and use it in preference to user_runhost.
     */

    if strcasecmp(sudo_user.runhost, sudo_user.host) == 0 as libc::c_int
        && get_ipa_hostname(&mut (*handle).ipa_shost, &mut (*handle).ipa_host)
            == -(1 as libc::c_int)
    {
        free(handle as *mut libc::c_void);
        debug_return_int!(ENOMEM);
    }

    /*if strcasecmp(sudo_user.runhost, sudo_user.host) == 0 as libc::c_int {
        if get_ipa_hostname(&mut (*handle).ipa_shost, &mut (*handle).ipa_host)
            == -(1 as libc::c_int)
        {
            free(handle as *mut libc::c_void);
            debug_return_int!(ENOMEM);
        }
    }*/

    /* The "parse tree" contains userspecs, defaults, aliases and hostnames. */
    init_parse_tree(
        &mut (*handle).parse_tree,
        (*handle).ipa_host,
        (*handle).ipa_shost,
    );
    (*nss).handle = handle as *mut libc::c_void;

    sudo_debug_printf!(
        SUDO_DEBUG_DEBUG,
        b"handle=%p\0" as *const u8 as *const libc::c_char,
        handle
    );

    debug_return_int!(0);
}

/*
 * Perform query for user and host and convert to sudoers parse tree.
 */
unsafe extern "C" fn sudo_sss_query(mut nss: *mut sudo_nss, mut pw: *mut passwd) -> libc::c_int {
    let mut handle: *mut sudo_sss_handle = (*nss).handle as *mut sudo_sss_handle;
    let mut sss_result: *mut sss_sudo_result = std::ptr::null_mut::<sss_sudo_result>();
    let mut ret: libc::c_int = 0 as libc::c_int;
    debug_decl!(SUDOERS_DEBUG_SSSD!());

    if handle.is_null() {
        sudo_debug_printf!(
            SUDO_DEBUG_ERROR,
            b"%s: called with NULL handle\0" as *const u8 as *const libc::c_char,
            get_function_name!()
        );
        debug_return_int!(-(1 as libc::c_int));
    }

    'done: loop {
        /* Use cached result if it matches pw. */
        if !((*handle).pw).is_null() {
            if pw == (*handle).pw {
                break 'done;
            }
            sudo_pw_delref((*handle).pw);
            (*handle).pw = std::ptr::null_mut::<passwd>();
        }
        /* Free old userspecs, if any. */
        free_userspecs(&mut (*handle).parse_tree.userspecs);

        /* Fetch list of sudoRole entries that match user and host. */
        sss_result = sudo_sss_result_get(nss, pw);

        sudo_debug_printf!(
            SUDO_DEBUG_DIAG,
            b"searching SSSD/LDAP for sudoers entries for user %s, host %s\0" as *const u8
                as *const libc::c_char,
            (*pw).pw_name,
            sudo_user.runhost
        );

        /* Stash a ref to the passwd struct in the handle. */
        sudo_pw_addref(pw);
        (*handle).pw = pw;

        /* Convert to sudoers parse tree if the user was found. */
        if !sss_result.is_null() && !sss_to_sudoers(handle, sss_result) {
            ret = -(1 as libc::c_int);
            break 'done;
        }

        /*if !sss_result.is_null() {
            if !sss_to_sudoers(handle, sss_result) {
                ret = -(1 as libc::c_int);
                break 'done;
            }
        }*/
        break;
    }

    //done:
    /* Cleanup */
    ((*handle).fn_free_result).expect("non-null function pointer")(sss_result);
    if ret == -(1 as libc::c_int) {
        free_userspecs(&mut (*handle).parse_tree.userspecs);
        if !((*handle).pw).is_null() {
            sudo_pw_delref((*handle).pw);
            (*handle).pw = std::ptr::null_mut::<passwd>();
        }
    }

    sudo_debug_printf!(
        SUDO_DEBUG_DIAG,
        b"Done with LDAP searches\0" as *const u8 as *const libc::c_char
    );

    debug_return_int!(ret);
}

/*
 * Return the initialized (but empty) sudoers parse tree.
 * The contents will be populated by the getdefs() and query() functions.
 */
unsafe extern "C" fn sudo_sss_parse(mut nss: *mut sudo_nss) -> *mut sudoers_parse_tree {
    let mut handle: *mut sudo_sss_handle = (*nss).handle as *mut sudo_sss_handle;
    debug_decl!(SUDOERS_DEBUG_SSSD!());

    if handle.is_null() {
        sudo_debug_printf!(
            SUDO_DEBUG_ERROR,
            b"%s: called with NULL handle\0" as *const u8 as *const libc::c_char,
            get_function_name!()
        );
        debug_return_ptr!(std::ptr::null_mut::<sudoers_parse_tree>());
    }

    debug_return_ptr!(&mut (*handle).parse_tree as *mut sudoers_parse_tree);
}

unsafe extern "C" fn sudo_sss_getdefs(mut nss: *mut sudo_nss) -> libc::c_int {
    let mut handle: *mut sudo_sss_handle = (*nss).handle as *mut sudo_sss_handle;
    let mut sss_result: *mut sss_sudo_result = std::ptr::null_mut::<sss_sudo_result>();
    static mut cached: bool = false;
    let mut sss_error: uint32_t = 0;
    let mut i: libc::c_uint = 0;
    let mut rc: libc::c_int = 0;
    debug_decl!(SUDOERS_DEBUG_SSSD!());

    if handle.is_null() {
        sudo_debug_printf!(
            SUDO_DEBUG_ERROR,
            b"%s: called with NULL handle\0" as *const u8 as *const libc::c_char,
            get_function_name!()
        );
        debug_return_ptr!(-(1 as libc::c_int));
    }

    /* Use cached result if present. */
    if cached {
        debug_return_ptr!(0);
    }

    sudo_debug_printf!(
        SUDO_DEBUG_DIAG,
        b"Looking for cn=defaults\0" as *const u8 as *const libc::c_char
    );

    /* NOTE: these are global defaults, user-ID and name are not used. */
    rc = ((*handle).fn_send_recv_defaults).expect("non-null function pointer")(
        (*sudo_user.pw).pw_uid,
        (*sudo_user.pw).pw_name,
        &mut sss_error,
        &mut (*handle).domainname,
        &mut sss_result,
    );
    match rc {
        0 => {}
        _ => {
            if rc == ENOMEM {
                sudo_warnx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }

            /* FALLTHROUGH */
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR,
                b"handle->fn_send_recv_defaults: rc=%d, sss_error=%u\0" as *const u8
                    as *const libc::c_char,
                rc,
                sss_error
            );
            debug_return_ptr!(-(1 as libc::c_int));
        }
    }

    'bad: loop {
        match sss_error as libc::c_int {
            0 => {
                /* Success */
                loop {
                    if i >= (*sss_result).num_rules {
                        break;
                    }
                    let mut sss_rule: *mut sss_sudo_rule = ((*sss_result).rules).offset(i as isize);
                    sudo_debug_printf!(
                        SUDO_DEBUG_DIAG,
                        b"Parsing cn=defaults, %d/%d\0" as *const u8 as *const libc::c_char,
                        i,
                        (*sss_result).num_rules
                    );
                    if !sudo_sss_parse_options(handle, sss_rule, &mut (*handle).parse_tree.defaults)
                    {
                        break 'bad;
                    }
                    i = i.wrapping_add(1);
                }
            }
            ENOENT => {
                sudo_debug_printf!(
                    SUDO_DEBUG_INFO,
                    b"No global defaults entry found in SSSD.\0" as *const u8
                        as *const libc::c_char
                );
            }
            _ => {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR,
                    b"sss_error=%u\n\0" as *const u8 as *const libc::c_char,
                    sss_error
                );
                break 'bad;
            }
        }
        ((*handle).fn_free_result).expect("non-null function pointer")(sss_result);
        cached = true;
        debug_return_int!(0);
    }
    //bad:
    ((*handle).fn_free_result).expect("non-null function pointer")(sss_result);
    debug_return_int!(-(1 as libc::c_int));
}

/* sudo_nss implementation */
#[no_mangle]
pub static mut sudo_nss_sss: sudo_nss = unsafe {
    {
        sudo_nss {
            entries: {
                C2RustUnnamed_7 {
                    tqe_next: 0 as *const sudo_nss as *mut sudo_nss,
                    tqe_prev: 0 as *const *mut sudo_nss as *mut *mut sudo_nss,
                }
                //init
            },
            open: Some(sudo_sss_open as unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int),
            close: Some(sudo_sss_close as unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int),
            parse: Some(
                sudo_sss_parse as unsafe extern "C" fn(*mut sudo_nss) -> *mut sudoers_parse_tree,
            ),
            query: Some(
                sudo_sss_query as unsafe extern "C" fn(*mut sudo_nss, *mut passwd) -> libc::c_int,
            ),
            getdefs: Some(sudo_sss_getdefs as unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int),
            handle: 0 as *const libc::c_void as *mut libc::c_void,
            parse_tree: 0 as *const sudoers_parse_tree as *mut sudoers_parse_tree,
            ret_if_found: false,
            ret_if_notfound: false,
        }
        //init
    }
};
